#include <Arduino.h>
#include "EdpKit.h"

/*
 * [说明]
 * Main.c 是为了测试EdpKit而写的, 也是给客户展示如何使用EdpKit
 * Main.c 使用的是c & linux socket
 *
 * 测试包含了：
 *      打包EDP包并发送：连接设备云请求, 心跳请求, 转发数据, 存储json数据, 存储bin数据
 *      接收并解析EDP包：连接设备云请求响应, 心跳请求响应, 转发数据, 存储json数据, 存储bin数据
 *
 * [注意]
 * Main.c不属于我们EDP SDK的一部分, 客户程序应该根据自己的系统写类似Main.c的代码
 * 客户程序应该只包含Common.h, cJSON.* 和 EdpKit.*, 而不应该包含Main.c
 * 
 * 加解密是利用openssl库实现的，如果有openssl库，则可以直接利用Openssl.*文件中提供
 * 的函数实现加解密。否则应该自己实现Openssl.h中的函数。
 * 如果需要加密功能，请参考Makefile中的说明，取消相应行的注释。
 */

/*----------------------------错误码-----------------------------------------*/
#define ERR_CREATE_SOCKET   -1 
#define ERR_HOSTBYNAME      -2 
#define ERR_CONNECT         -3 
#define ERR_SEND            -4
#define ERR_TIMEOUT         -5
#define ERR_RECV            -6
/*---------------统一linux和windows上的Socket api----------------------------*/
#ifndef htonll
#ifdef _BIG_ENDIAN
#define htonll(x)   (x)
#define ntohll(x)   (x)
#else
#define htonll(x)   ((((uint64)htonl(x)) << 32) + htonl(x >> 32))
#define ntohll(x)   ((((uint64)ntohl(x)) << 32) + ntohl(x >> 32))
#endif
#endif
/* linux程序需要定义_LINUX */
#ifdef _LINUX
#define Socket(a,b,c)          socket(a,b,c)
#define Connect(a,b,c)         connect(a,b,c)
#define Close(a)               close(a)
#define Read(a,b,c)            read(a,b,c)
#define Recv(a,b,c,d)          recv(a, (void *)b, c, d)
#define Select(a,b,c,d,e)      select(a,b,c,d,e)
#define Send(a,b,c,d)          send(a, (const int8 *)b, c, d)
#define Write(a,b,c)           write(a,b,c)
#define GetSockopt(a,b,c,d,e)  getsockopt((int)a,(int)b,(int)c,(void *)d,(socklen_t *)e)
#define SetSockopt(a,b,c,d,e)  setsockopt((int)a,(int)b,(int)c,(const void *)d,(int)e)
#define GetHostByName(a)       gethostbyname((const char *)a)
#endif

static int g_is_encrypt = 0;
EdpPacket* send_pkg;
EDPKit edpkit;

/*
 * buffer按十六进制输出
 */
void hexdump(const unsigned char *buf, uint32 num)
{
    uint32 i = 0;
    for (; i < num; i++) 
    {
        Serial.println("%02X ", buf[i]);
        if ((i+1)%8 == 0) 
            Serial.println("\n");
    }
    Serial.println("\n");
}

/* 
 * 函数名:  DoSend
 * 功能:    将buffer中的len字节内容写入(发送)socket描述符sockfd, 成功时返回写的(发送的)字节数.
 * 参数:    sockfd  socket描述符 
 *          buffer  需发送的字节
 *          len     需发送的长度
 * 说明:    这里只是给出了一个发送数据的例子, 其他方式请查询相关socket api
 *          一般来说, 发送都需要循环发送, 是因为需要发送的字节数 > socket的写缓存区时, 一次send是发送不完的.
 * 相关socket api:  
 *          send
 * 返回值:  类型 (int32)
 *          <=0     发送失败
 *          >0      成功发送的字节数
 */
int32 DoSend(int32 sockfd, const char* buffer, uint32 len)
{
    int32 total  = 0;
    int32 n = 0;
    while (len != total)
    {
        /* 试着发送len - total个字节的数据 */
        n = Send(sockfd,buffer + total,len - total,MSG_NOSIGNAL);
        if (n <= 0)
        {
            fSerial.println(stderr, "ERROR writing to socket\n");
            return n;
        }
        /* 成功发送了n个字节的数据 */
        total += n;
    }
    /* wululu test print send bytes */
    hexdump((const unsigned char *)buffer, len);
    return total;
}
/* 
 * 函数名:  recv_data_func
 * 功能:    接收线程函数
 * 参数:    arg     socket描述符
 * 说明:    这里只是给出了一个从socket接收数据的例子, 其他方式请查询相关socket api
 *          一般来说, 接收都需要循环接收, 是因为需要接收的字节数 > socket的读缓存区时, 一次recv是接收不完的.
 * 相关socket api:  
 *          recv
 * 返回值:  无
 */
void recv_data_func(void* arg)
{
    int sockfd = *(int*)arg;
    int error = 0;
    int n, rtn;
    uint8 mtype, jsonorbin;
    char buffer[1024];
    RecvBuffer* recv_buf = NewBuffer();
    EdpPacket* pkg;
    
    char* src_devid;
    char* push_data;
    uint32 push_datalen;

    cJSON* save_json;
    char* save_json_str;

    cJSON* desc_json;
    char* desc_json_str;
    char* save_bin; 
    uint32 save_binlen;
    unsigned short msg_id;
    unsigned char save_date_ret;

    char* cmdid;
    uint16 cmdid_len;
    char*  cmd_req;
    uint32 cmd_req_len;
    EdpPacket* send_pkg;
    char* ds_id;
    double dValue = 0;
    int iValue = 0;
    char* cValue = NULL;

    char* simple_str = NULL;
    char cmd_resp[] = "ok";
    unsigned cmd_resp_len = 0;

	DataTime stTime = {0};

    FloatDPS* float_data = NULL;
    int count = 0;
    int i = 0;

    struct UpdateInfoList* up_info = NULL;

#ifdef _DEBUG
    Serial.println("[%s] recv thread start ...\n", __func__);
#endif

    while (error == 0)
    {
        /* 试着接收1024个字节的数据 */
        n = Recv(sockfd, buffer, 1024, MSG_NOSIGNAL);
        if (n <= 0)
            break;
        Serial.println("recv from server, bytes: %d\n", n);
        /* wululu test print send bytes */
        hexdump((const unsigned char *)buffer, n);
        /* 成功接收了n个字节的数据 */
        WriteBytes(recv_buf, buffer, n);
        while (1)
        {
            /* 获取一个完成的EDP包 */
            if ((pkg = GetEdpPacket(recv_buf)) == 0)
            {
                Serial.println("need more bytes...\n");
                break;
            }
            /* 获取这个EDP包的消息类型 */
            mtype = EdpPacketType(pkg);
#ifdef _ENCRYPT
            if (mtype != ENCRYPTRESP){
                if (g_is_encrypt){
                    SymmDecrypt(pkg);
                }
            }
#endif
            /* 根据这个EDP包的消息类型, 分别做EDP包解析 */
            switch(mtype)
            {
#ifdef _ENCRYPT
            case ENCRYPTRESP:
                UnpackEncryptResp(pkg);
                break;
#endif
            case CONNRESP:
                /* 解析EDP包 - 连接响应 */
                rtn = edpkit.UnpackConnectResp(pkg);
                Serial.println("recv connect resp, rtn: %d\n", rtn);
                break;
            case PUSHDATA:
                /* 解析EDP包 - 数据转发 */
                edpkit.UnpackPushdata(pkg, &src_devid, &push_data, &push_datalen);
                Serial.println("recv push data, src_devid: %s, push_data: %s, len: %d\n",
                       src_devid, push_data, push_datalen);
                free(src_devid);
                free(push_data);
                break;
            case UPDATERESP:
                edpkit.UnpackUpdateResp(pkg, &up_info);
                while (up_info){
                    Serial.println("name = %s\n", up_info->name);
                    Serial.println("version = %s\n", up_info->version);
                    Serial.println("url = %s\nmd5 = ", up_info->url);
                    for (i=0; i<32; ++i){
                        Serial.println("%c", (char)up_info->md5[i]);
                    }
                    Serial.println("\n");
                    up_info = up_info->next;
                }
                edpkit.FreeUpdateInfolist(up_info);
                break;

            case SAVEDATA:
                /* 解析EDP包 - 数据存储 */
                if (edpkit.UnpackSavedata(pkg, &src_devid, &jsonorbin) == 0)
                {
                    if (jsonorbin == kTypeFullJson
                        || jsonorbin == kTypeSimpleJsonWithoutTime
                        || jsonorbin == kTypeSimpleJsonWithTime)
                    {
                        Serial.println("json type is %d\n", jsonorbin);
                        /* 解析EDP包 - json数据存储 */
                        /* UnpackSavedataJson(pkg, &save_json); */
                        /* save_json_str=cJSON_Print(save_json); */
                        /* Serial.println("recv save data json, src_devid: %s, json: %s\n", */
                        /*     src_devid, save_json_str); */
                        /* free(save_json_str); */
                        /* cJSON_Delete(save_json); */

                        /* UnpackSavedataInt(jsonorbin, pkg, &ds_id, &iValue); */
                        /* Serial.println("ds_id = %s\nvalue= %d\n", ds_id, iValue); */

                        edpkit.UnpackSavedataDouble(jsonorbin, pkg, &ds_id, &dValue);
                        Serial.println("ds_id = %s\nvalue = %f\n", ds_id, dValue);

                        /* UnpackSavedataString(jsonorbin, pkg, &ds_id, &cValue); */
                        /* Serial.println("ds_id = %s\nvalue = %s\n", ds_id, cValue); */
                        /* free(cValue); */

                        free(ds_id);
				
                    }
                    else if (jsonorbin == kTypeBin)
                    {/* 解析EDP包 - bin数据存储 */
                        edpkit.UnpackSavedataBin(pkg, &desc_json, (uint8**)&save_bin, &save_binlen);
                        desc_json_str=cJSON_Print(desc_json);
                        Serial.println("recv save data bin, src_devid: %s, desc json: %s, bin: %s, binlen: %d\n",
                               src_devid, desc_json_str, save_bin, save_binlen);
                        free(desc_json_str);
                        cJSON_Delete(desc_json);
                        free(save_bin);
                    }
                    else if (jsonorbin == kTypeString ){
                        edpkit.UnpackSavedataSimpleString(pkg, &simple_str);
			    
                        Serial.println("%s\n", simple_str);
                        free(simple_str);
                    }else if (jsonorbin == kTypeStringWithTime){
						edpkit.UnpackSavedataSimpleStringWithTime(pkg, &simple_str, &stTime);
			    
                        Serial.println("time:%u-%02d-%02d %02d-%02d-%02d\nstr val:%s\n", 
							stTime.year, stTime.month, stTime.day, stTime.hour, stTime.minute, stTime.second, simple_str);
                        free(simple_str);
					}else if (jsonorbin == kTypeFloatWithTime){
                        if(edpkit.UnpackSavedataFloatWithTime(pkg, &float_data, &count, &stTime)){
                            Serial.println("UnpackSavedataFloatWithTime failed!\n");
                        }

                        Serial.println("read time:%u-%02d-%02d %02d-%02d-%02d\n", 
                            stTime.year, stTime.month, stTime.day, stTime.hour, stTime.minute, stTime.second);
                        Serial.println("read float data count:%d, ptr:[%p]\n", count, float_data);
                        
                        for(i = 0; i < count; ++i){
                            Serial.println("ds_id=%u,value=%f\n", float_data[i].ds_id, float_data[i].f_data);
                        }

                        free(float_data);
                        float_data = NULL;
                    }
                    free(src_devid);
                }else{
                    Serial.println("error\n");
                }
                break;
            case SAVEACK:
                edpkit.UnpackSavedataAck(pkg, &msg_id, &save_date_ret);
                Serial.println("save ack, msg_id = %d, ret = %d\n", msg_id, save_date_ret);
                break;
            case CMDREQ:
                if (edpkit.UnpackCmdReq(pkg, &cmdid, &cmdid_len,
                                 &cmd_req, &cmd_req_len) == 0){
                    /*
                     * 用户按照自己的需求处理并返回，响应消息体可以为空，此处假设返回2个字符"ok"。
                     * 处理完后需要释放
                     */
                    cmd_resp_len = strlen(cmd_resp);
                    send_pkg = edpkit.PacketCmdResp(cmdid, cmdid_len,
                                             cmd_resp, cmd_resp_len);
#ifdef _ENCRYPT
                    if (g_is_encrypt){
                        SymmEncrypt(send_pkg);
                    }
#endif
                    DoSend(sockfd, (const char*)send_pkg->_data, send_pkg->_write_pos);
                    DeleteBuffer(&send_pkg);
		    
                    free(cmdid);
                    free(cmd_req);
                }
                break;
            case PINGRESP:
                /* 解析EDP包 - 心跳响应 */
                UnpackPingResp(pkg);
                Serial.println("recv ping resp\n");
                break;

            default:
                /* 未知消息类型 */
                error = 1;
                Serial.println("recv failed...\n");
                break;
            }
            DeleteBuffer(&pkg);
        }
    }
    DeleteBuffer(&recv_buf);

#ifdef _DEBUG
    Serial.println("[%s] recv thread end ...\n", __func__);
#endif
}

void usage(char* name){
    Serial.println("Usage:%s [options]\n", basename(name));
    Serial.println("-h List help document\n");
    Serial.println("-i The ip of the edpacc\n");
    Serial.println("-p The port of the edpacc\n");
    Serial.println("-s Assign the dev_id of the source\n");
    Serial.println("-d Assign the dev_id of the destination\n");
    Serial.println("-a Assign the API key of the source\n");
    Serial.println("-l Assign the name of the datastream for test 'save json data'\n" );
    Serial.println("-v Assign the value of the datastream for test 'save json data'\n");
	Serial.println("-t Assign the time of the datastream for test 'save string data'\n");
    Serial.println("-E Encrypt\n");
    exit(0);
}

void setup(void)
{
    /* create a socket and connect to server */
    sockfd = Open((const uint8*)ip, atoi(port));
    if (sockfd < 0) 
        exit(0);
    /* connect to server */
    /*    send_pkg = PacketConnect1(src_dev, "Bs04OCJioNgpmvjRphRak15j7Z8=");*/
    send_pkg = PacketConnect1(src_dev, src_api_key);

    /* send_pkg = PacketConnect2("433223", "{ \"SYS\" : \"0DEiuApATHgLurKNEl6vY4bLwbQ=\" }");*/
    /* send_pkg = PacketConnect2("433223", "{ \"13982031959\" : \"888888\" }");*/

    /* 向设备云发送连接请求 */
    Serial.println("send connect to server, bytes: %d\n", send_pkg->_write_pos);
    ret=DoSend(sockfd, (const char*)send_pkg->_data, send_pkg->_write_pos);
    DeleteBuffer(&send_pkg);
}

void loop(void)
{
    recv_data_func(NULL);

}

int main(int argc, char *argv[])
{
    char opt;
    int sockfd, n, ret;
    pthread_t id_1;
    
    char c;
    char push_data[] = {'a','b','c'};
	char text1[]="{\"name\": \"Jack\"}";
    /* cJSON中文只支持unicode编码   */
	char text2[]="{\"ds_id\": \"temperature\"}";	
    cJSON *save_json, *desc_json;
    char save_bin[] = {'c', 'b', 'a'};

    char* ip = NULL;
    char* port = NULL;
    char* src_dev = NULL;
    char* dst_dev = NULL;
    char* src_api_key = NULL;
    char* ds_for_send = NULL;
    double value_for_send = 0.0;
	DataTime save_time = {0};
    char send_str[] = ",;temperature,2015-03-22 22:31:12,22.5;humidity,35%;pm2.5,89;1001";
    FloatDPS send_float[] = {{1,0.5},{2,0.8},{3,-0.5}};
    SaveDataType data_type;
    struct UpdateInfoList* up_info = NULL;

    while (1)
    {
        c = getchar();
        switch (c){
        case '0':
            send_pkg = edpkit.PacketPing();
            break;

        case '1':
            send_pkg = edpkit.PacketPushdata(dst_dev, push_data, sizeof(push_data));
            break;

        case '2':
        case '4':
        case '5':
            if (c == '2') data_type = kTypeFullJson;
            if (c == '4') data_type = kTypeSimpleJsonWithoutTime;
            if (c == '5') data_type = kTypeSimpleJsonWithTime;

            send_pkg = edpkit.PacketSavedataInt(data_type, dst_dev, ds_for_send, 1234, 0, 0);
            /* send_pkg = PacketSavedataDouble(data_type, dst_dev, ds_for_send, value_for_send, 0, 0); */
            /* send_pkg = PacketSavedataString(data_type, dst_dev, ds_for_send, "test12345678", 0, 1); */
            break;
	    
        case '3':
            desc_json=cJSON_Parse(text2);
            send_pkg = edpkit.PacketSavedataBin(dst_dev, desc_json, (const uint8*)save_bin, sizeof(save_bin), 0);
            break;

        case '6':
            send_pkg = edpkit.PacketSavedataSimpleString(dst_dev, send_str, 0);
            break;

		case '7':
			if (save_time.year > 0)
				send_pkg = edpkit.PacketSavedataSimpleStringWithTime(dst_dev, send_str, &save_time, 0);
			else
				send_pkg = PacketSavedataSimpleStringWithTime(dst_dev, send_str, NULL, 0);
			break;

        case '8':
            if (save_time.year > 0)
			    send_pkg = PackSavedataFloatWithTime(dst_dev, send_float, 3, &save_time, 0);
		    else
			    send_pkg = PackSavedataFloatWithTime(dst_dev, send_float, 3, NULL, 0);
            break;

        case '9':
            /* up_info = (struct UpdateInfoList*)malloc(sizeof(struct UpdateInfoList)); */
            /* up_info->name = "file.txt"; */
            /* up_info->version = "v1.0"; */
            /* up_info->next = NULL; */

            /* up_info->next = (struct UpdateInfoList*)malloc(sizeof(struct UpdateInfoList)); */
            /* up_info->next->name = "EdpKit.h"; */
            /* up_info->next->version = "v1.0"; */
            /* up_info->next->next = NULL; */

            send_pkg = PacketUpdateReq(up_info);
            hexdump(send_pkg->_data, send_pkg->_write_pos);
            free(up_info);
            break;

        default:
            getchar();	/* 读取回车符，丢弃 */
            Serial.println("input error, please try again\n");
            continue;
        }
#ifdef _ENCRYPT
	    if (g_is_encrypt){
            SymmEncrypt(send_pkg);
	    }
#endif
        Serial.println("%s, bytes: %d\n", msg[c-'0'], send_pkg->_write_pos);
        DoSend(sockfd, (const char*)send_pkg->_data, send_pkg->_write_pos);
        DeleteBuffer(&send_pkg);

	    getchar(); /* 读取回车符，丢弃 */
    }
    /* close socket */
    Close(sockfd);

    pthread_join(id_1,NULL);
    return 0;
}
